package com.hspedu.seckill.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hspedu.seckill.mapper.OrderMapper;
import com.hspedu.seckill.pojo.Order;
import com.hspedu.seckill.pojo.SeckillGoods;
import com.hspedu.seckill.pojo.SeckillOrder;
import com.hspedu.seckill.pojo.User;
import com.hspedu.seckill.service.OrderService;
import com.hspedu.seckill.service.SeckillGoodsService;
import com.hspedu.seckill.service.SeckillOrderService;
import com.hspedu.seckill.util.MD5Util;
import com.hspedu.seckill.util.UUIDUtil;
import com.hspedu.seckill.vo.GoodsVo;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    //装配SeckillGoodsService
    @Resource
    private SeckillGoodsService seckillGoodsService;

    //装配SeckillOrderService
    @Resource
    private SeckillOrderService seckillOrderService;

    //装配RedisTemplate
    @Resource
    private RedisTemplate redisTemplate;


    //完成秒杀
    @Transactional
    @Override
    public Order seckill(User user, GoodsVo goodsVo) {

        // SeckillGoods seckillGoods = seckillGoodsService.getOne(new QueryWrapper<SeckillGoods>().eq("goods_id", goodsVo.getId()));

        //完成一个基本的秒杀操作，后面在高并发的情况下再去优化   目前不具有原子性
        // seckillGoods.setStockCount(seckillGoods.getStockCount()-1);
        // seckillGoodsService.updateById(seckillGoods);

        //1. mysql在默认的事务隔离级别[REPEATABLE-READ]下
        //2. 执行update语句时，会在事务中锁定要更新的行
        //3. 这样可以防止其他会话在同一行执行update，delete

        //只有在更新成功时，返回true，否则为false，及更新后受影响的行数大于1才为true
        //查询秒杀商品的库存，并-1
        System.out.println("执行update--->" + user.getId());

        boolean update = seckillGoodsService.update(new UpdateWrapper<SeckillGoods>()
                .setSql("stock_count=stock_count-1")
                .eq("goods_id", goodsVo.getId())
                .gt("stock_count", 0));

        if (!update) {//如果更新库存失败，说明已经没有库存了
            //将秒杀结果存到redis
            redisTemplate.opsForValue().set("seckill:" + user.getId() + ":" + goodsVo.getId(), -1);
            return null;
        }

        //生成普通订单
        Order order = new Order();

        order.setUserId(user.getId());
        order.setGoodsId(goodsVo.getId());
        order.setDeliveryAddrId(0L);//设置一个初始值
        order.setGoodsName(goodsVo.getGoodsName());
        order.setGoodsCount(1);
        order.setGoodsPrice(goodsVo.getSeckillPrice());
        order.setOrderChannel(1);//设置一个初始值
        order.setStatus(0);//设置一个初始值 未支付
        order.setCreateDate(new Date());//设置成当前时间

        //保存order
        this.save(order);

        //生成秒杀商品订单
        SeckillOrder seckillOrder = new SeckillOrder();
        seckillOrder.setUserId(user.getId());
        seckillOrder.setOrderId(order.getId());
        seckillOrder.setGoodsId(order.getGoodsId());

        //保存seckillOrder
        seckillOrderService.save(seckillOrder);

        //将生成的秒杀订单，存入到redis，这样在查询   某个用户是否已经秒杀了这个商品时直接到redis中查询，起到优化效果
        //设计秒杀订单的key => order:用户id:商品id
        redisTemplate.opsForValue().set("order:" + user.getId() + ":" + goodsVo.getId(), seckillOrder);
        //将秒杀结果存到redis
        redisTemplate.opsForValue().set("seckill:" + user.getId() + ":" + goodsVo.getId(), seckillOrder.getId());

        return order;
    }

    //生成秒杀唯一秒杀路径
    @Override
    public String createPath(User user, Long goodsId) {
        //生成秒杀路径
        String path = MD5Util.md5(UUIDUtil.uuid());

        //将随机生成的路径保存到redis，设置一个超时时间60s
        //key的设计：seckillPath:userId:goodsId
        redisTemplate.opsForValue().set("seckillPath:" + user.getId() + ":" + goodsId, path, 60, TimeUnit.SECONDS);
        return path;
    }

    //对秒杀路径进行校验
    @Override
    public boolean checkPath(User user, Long goodsId, String path) {
        if (null == user || goodsId < 0 || !StringUtils.hasText(path)) {
            return false;
        }

        //取出该用户秒杀该商品的路径
        String redisPath = (String) redisTemplate.opsForValue().get("seckillPath:" + user.getId() + ":" + goodsId);

        return path.equals(redisPath);
    }

    //验证用户输入的验证码是否正确
    @Override
    public boolean checkCaptcha(User user, Long goodsId, String captcha) {
        if (null == user || goodsId < 0 || !StringUtils.hasText(captcha)) {
            return false;
        }

        //从redis取出验证码
        String redisCaptcha = (String) redisTemplate.opsForValue().get("captcha:" + user.getId() + ":" + goodsId);

        return captcha.equals(redisCaptcha);
    }
}
